Welcome.
In this lecture we're going to look at a concept in object oriented programming called encapsulation.
Encapsulation is the idea that we should encapsulate functions and methods together into a single unit,
which we've been doing with our classes.
But we should also limit access to the data when needed.
So if there's any properties in an object we create, we want to limit access to that data.
And we may also want to limit changes to that data as well.
In our previous lectures, you may have seen how we've used the underscore underscore index meta method
directly within the class itself, and use the class as the meta table.
However, the problem with doing this means that we can create new objects by calling the new function
from that object itself, which sometimes we don't want to do.
So as an example, if I had a human class.
And I set the underscore underscore index meta method to itself.
And then I had a constructor for this class.
And we'll just return a new table.
Whatever.
Or actually, we return a new table with a meta table attached to it, which is the human class.
If we were to create a new human.
From this point.
This new human also has the constructor inside of it, which we don't want to happen.
We only want to be able to access the constructor from the main class or the table itself.
So how can we avoid doing this?
Well, what we could do is we could create a second table and I'm going to call it human meta table.
And the purpose of this table is to act as our meta table for creating new human objects, and as well
to store all of the functions that the human class is going to use.
The only purpose of this table is going to hold our constructor, and that's it.
We're not going to put any other implementation in here.
It's just going to hold our constructor.
So that means when we create a new human object we can set it to the human meta table.
And we'll make sure to get rid of this.
And instead set the underscore underscore index meta method inside of the human meta table to itself.
Now, if we were to pass a name to create for this new human, we could do that just right here.
I'll create a table called self.
And actually we'll just copy this here and put it there.
And then inside of this human meta table, I can also define, um, what properties a human table should
have.
So like the name.
We'll leave it blank for now.
And the from here we can set the name equal to the name passed to the function and then return self.
Now by doing it this way.
If I were to create a new human.
Human dot new and pass a name like Bob.
I'm no longer able to access the dot new function.
I can only do it from the human class.
No longer am I able to do it from the object itself, which is good.
We fixed that problem.
However, let's say another problem is that I don't want people adding new properties into this table.
I don't want that to happen.
Perhaps I only want it to have a name and nothing else.
But in its current state, I can add whatever properties I want into this table.
I can add age.
I can add, you know, any, um, any key value pairs into this table.
How could I avoid doing that?
Well, inside of the human meta table, I could set a new meta method called new index, which gets
fired when we try to create a new index in a table.
And what we could do is set it to equal to a function.
And what we'll do is we'll just raise an error and say something like unable to add new index to this
object.
And that means if I try to add a new property like age to my human, it should error.
And there we go.
Unable to add new index to this object.
So just like that, we've disabled the ability for us to create new, um, new indexes within our human
object.
Now, let's also say I want to prevent us from being able to change the name directly from the table
itself, because in its current state, I can rename this to whatever I want.
But sometimes that kind of functionality isn't desired.
So how can we avoid this?
Well, another way we could do it is that we could create a table, and I'm going to call it private
data.
And instead of setting the name directly within the object itself, what I could do is I could create
a new table in here.
I'll call it private.
And I'll set the name for our human inside of this table instead.
And then from that point.
I could create a new key value pair within the private data table.
Private data.
The key for this private data table is going to be the object itself.
And then I'm going to set it equal to this private table right here.
So that means if I ever wanted to access the name of my human, I would have to have access to this
private data table, which can only be accessed if you have the object itself.
So that means I'm no longer able to access the name property because it exists within this private data
table.
But let's say I want to be able to change the name from a function.
Well, what I could do is I could create a function in the human meta table.
We're going to call it change name.
And what this function is going to do is it's going to get that private table we created here that is
stored in the private data table.
We're going to get it from the private data table and past self.
Because remember self is the key for this table.
And from that point we can set private dot name equal to the new name that is passed to this function.
Equal to name.
And then I'm also going to create a function called speak.
So human meta table speak.
And what this can do is print out.
We get the private data table.
We pass self to it as the key and we can get the name.
And we'll print something like Bob says hi.
And now when I call this function on my new human speak, it should print.
Bob says hi.
So let's run that.
We get?
Bob says hi.
Perfect.
And now that means I should also remove the name property from my human meta table.
So I've removed it from the meta table, and now the name only exists within this private table that
gets stored within this private data table.
That means in my new human, I can no longer access the name.
It's not possible for me to access it anymore.
But since I've created this change name table, I can call it on my new human change name.
And I could change it to something like Jerry.
So instead it should print.
Jerry says hi instead of Bob says hi.
So if we hit run.
There we go.
Jerry says hi.
So this is the main idea of encapsulation, limiting access to data and grouping functions together
into a single unit.
Because I don't want the constructor, the new function, to be part of any object that I create from
the class, I put it alone into its own table, and then every other function that I need to use on
the objects themselves like to speak or change the name.
It is stored in a separate table that is actually used as the meta table.
So let's go through this script one more time to make sure you understand everything.
So our human table, which is acting as our class, only has the constructor inside of it.
And inside this constructor we create a new table and we set it to this human meta table.
And this human meta table is the one that has the index meta methods and has these other functions for
changing the name and having the human speak.
After that, we create a new table called private, and instead of setting the name of our human directly
on this table, the self that gets returned, we store it in this private table.
And then we store this private table and another table called private data, whose key is the table
itself, the object itself.
And that's how we can get access to the data like the name.
That means when we go to change the name of our object, it has to get the table itself.
It references it from the private data table, getting its key because self is passed to this function.
And then from that point we can change the name to this new name passed to the function.
So that means when we create a new human right here and give it the name Bob, it's going to store the
name Bob inside of this private table which is stored in this private data table.
And any time we go to change the name, we have to call this function because the name is not stored
directly within the object, but instead inside this table.
So it goes, gets the table and then changes the name.
And then from that point we can use the speak function.
And then we can get the name again from accessing the private data table.
And then we can say something like says hi.
So when you take all this functionality, you want to store it inside of a module script.
And because you're storing it inside of a module script, no other script will be able to have access
to this data here because it is local.
It's hidden away from all the other scripts that require the module script.
So this lecture may have been a little bit confusing, but again, the main idea of encapsulation is
to limit access to data and group functions together into a single unit.
See you in the next lecture.